from operator import and_
import os
from pathlib import Path
import shutil
from time import time
from flask import current_app, g
from app.libs.forms import GetResourceListForm, IDForm, ResourceByIDForm, SearchForm, UploadPureVideoForm, UploadPureVideoForm
from vsearcher import VSearcher
from app.models.chapter import Chapter
from app.models.course import Course
from app.models.video import Video
from app.vo import BaseVO, ChapterCatalogVO, CourseCatalogVO, SVideoVO, VideoCatalogVO
from .base import BaseService
from vsearcher._core import vo as vs_vo
import time


class VideoService(BaseService):
    """
        功能：
            1. 获取对象 detail & pure
            2. 根据o_path获取对象
    """
    @classmethod
    def get_by_id(cls, form: ResourceByIDForm) -> Video:
        video_id = form.data.get('id')
        video = cls.optional_404(
            Video.current_user_filter(Video.id == video_id).first())
        # 1. video.o_path 获取真实的对象
        # 2. 读取kfs，生成cw赋值，需要保存cw属性，频繁读取怎么办缓存啊
        # 3. 返回
        o = VSearcher.loadResource(video.o_path)
        video.kfs = o.kfs
        return video

    @classmethod
    def save2db(cls, video: vs_vo.VideoVO, chapter_id=None, course_id=None) -> Video:
        new_video = Video()
        new_video.set_attr(video, filter_keys=['id'])
        new_video.user_id = g.current_user.id
        new_video.course_id = course_id  # 这里的 None有问题
        new_video.chapter_id = chapter_id
        Video.session.add(new_video)
        Video.session.flush()
        return new_video

    @classmethod
    def __getVideoOutputDir(cls, chapter_id, course_id):
        if course_id is not None:
            return cls.optional_404(
                Course.current_user_filter(Course.id == course_id).first()
            ).output_dir

        if chapter_id is not None:
            return cls.optional_404(
                Chapter.current_user_filter(Chapter.id == chapter_id).first()
            ).output_dir

        if chapter_id is None and course_id is None:
            return None

    @classmethod
    def upload_by_file(cls, form: UploadPureVideoForm):
        # 保存文件
        with Video.auto_commit():  # 事务
            file = form.data.get('file')
            temp_dir = Path(current_app.config['VIDEO_TEMP_DIR']).joinpath(
                f'{time.time()}')
            os.makedirs(temp_dir)
            temp_file_path = temp_dir.joinpath(file.filename)
            # @RISK 多个线程访问同一个视频文件，所有视频文件需要隔离，或者文件名随机前缀
            file.save(temp_file_path)  # 保存文件
            # 获取输出路径, 章节, 课程, 单独视频的输出路径是不一样的
            video_output_dir = cls.__getVideoOutputDir(
                form.chapter_id.data, form.course_id.data)
            vs_video_vo = VSearcher.executeVideo(
                str(temp_file_path),
                step=form.vsConfigForm.step.data,
                output_dir=video_output_dir,
                speed_x=form.vsConfigForm.speed.data)  # OCR处理
            # 如果存在，实施方案，Video.current_user_filter(Video.url == vs_video_vo.url).first()
            new_video = cls.save2db(
                video=vs_video_vo,
                chapter_id=form.chapter_id.data,
                course_id=form.course_id.data
            )  # 构建Video对象存入数据库
            # 处理之后, 删除文件
            shutil.rmtree(temp_dir, ignore_errors=True)
            return new_video

    @classmethod
    def searchByForm(cls, form: SearchForm) -> SVideoVO:
        video = cls.optional_404(
            Video.current_user_filter(Video.id == form.id.data).first()
        )
        return cls.search(form.key.data, video)

    @classmethod
    def search(cls, key, video: Video) -> SVideoVO:
        vs_video = VSearcher.search(video.o_path, key=key)
        vs_video.id = video.id
        vs_video.name = video.name
        return SVideoVO(vs_video)

    @classmethod
    def del_by_id(cls, form: ResourceByIDForm):
        # @RISK 如果一个视频属于某个Chapter 或者 Source 还要更新pickle对象中的内容, 比如删除了某一个上街
        # @WAIT vsearch 支持, 删除某个课程中的某个内容
        video = cls.optional_404(Video.current_user_filter(
            Video.id == form.id.data).first())
        with Video.auto_commit() as context:
            context._session.delete(video)
        # VSearcher.releaseResource(video.o_path)  # 删除对象的处理结果： 包括：图片
            VSearcher.releaseByOutputDir(video.output_dir)
            return video

    # @WAIT 多文件的上传

    @classmethod
    def gets(cls, form: GetResourceListForm):
        return Video.current_user_filter(and_(Video.chapter_id == None, Video.course_id == None)).offset(form.pager.offset).limit(form.pager.limit).all(), form.pager

    @classmethod
    def get_catalog(cls, form: IDForm) -> BaseVO:
        video = cls.optional_404(Video.current_user_filter(
            Video.id == form.id.data).first())
        if video.course_id:
            return CourseCatalogVO(video.chapter.course)
        elif video.chapter_id:
            return ChapterCatalogVO(video.chapter)
        else:
            return VideoCatalogVO(video)
